package org.vaadinetherealintegration.client.example3;

import etrl.geometry.Drawable;
import etrl.geometry.IndexArray;
import etrl.geometry.PrimitiveKind;
import etrl.geometry.Vertex;
import etrl.geometry.VertexArray;
import etrl.vecmath.Vector2f;
import etrl.vecmath.Vector3f;
import etrl.vecmath.Vector4f;

public class Character extends Drawable
{
	public Character()
	{
		super(PrimitiveKind.TRIANGLES);
	
		generateSphereGeometryBuffers(15, 15, 0.5f, new Vector4f(1.f, 1.f, 1.f, 1.f));
	}
	
	private void generateSphereGeometryBuffers(int sliceCount, int slideCount, float scale, Vector4f color)
	{
		int faceCount;
		
		if(sliceCount >= 2 && slideCount >= 4)
		{	   
			int verticeCount = (sliceCount - 1) * slideCount + 2;
			float[] vertice = new float[4 * verticeCount];
			float[] normals = new float[3 * verticeCount];

			faceCount = (sliceCount - 2) * (slideCount * 2) + 2 * slideCount;
			int [] indice = new int[faceCount * 3];

			float alphaStep = 360.f / slideCount;
			float betaStep = 180.f / sliceCount;
			int indexCount = 0;
			int faceIndex = 0;
			
			for(int cbeta = 0; cbeta <= sliceCount; ++cbeta)
			{
				for(int calpha = 0; calpha < slideCount; ++calpha)
				{
					double alpha = (alphaStep * calpha) * Math.PI / 180.f;
					double beta = (90.f - betaStep * cbeta) * Math.PI / 180.f;
					
					Vector3f P = new Vector3f();
					P.x = (float) (Math.cos(alpha) * Math.cos(beta) * scale);
					P.y = (float) (Math.sin(alpha) * Math.cos(beta) * scale);
					P.z = (float) (Math.sin(beta) * scale);
					
					if(cbeta != 0) // not bottom point
					{
						if(cbeta != sliceCount) // middle slice
						{
							if(cbeta == 1) // first slice, only one triangle per point, all using bottom point
							{
								indice[3 * faceIndex] 		= 0;
								indice[3 * faceIndex + 1] 	= indexCount;
								indice[3 * faceIndex + 2]	= (indexCount % sliceCount) + 1;
								faceIndex++;
							}
							else // else use points from previous slice
							{
								int indiceP1 = calpha + (cbeta - 1) * slideCount + 1; // current slice
								int indiceP2 = (calpha + 1) % slideCount + (cbeta - 1) * slideCount + 1; // next point on slice
								int indiceP3 = (calpha + 1) % slideCount + (cbeta - 1) * slideCount + 1 - slideCount; // next point on prev slice
								int indiceP4 = calpha + (cbeta - 1) * slideCount + 1 - slideCount; // previous slice
								
								indice[3 * faceIndex]		= indiceP1;                                                                                                                                  // currentPoint
								indice[3 * faceIndex + 1]	= indiceP2;
								indice[3 * faceIndex + 2]	= indiceP4;
								faceIndex++;
								
								indice[3 * faceIndex]		= indiceP2;                                                                                                                                  // currentPoint
								indice[3 * faceIndex + 1]	= indiceP4;
								indice[3 * faceIndex + 2]	= indiceP3;
								faceIndex++;							
							}
							
							// if it is the last slice ( the one under the top point ), make faces with the top point too
							if(cbeta == sliceCount - 1)
							{
								indice[3 * faceIndex]		= (sliceCount - 1) * slideCount + 1; 							// top point                                                                                                                                  // currentPoint
								indice[3 * faceIndex + 1]	= calpha + (cbeta - 1) * slideCount + 1; 						// current point
								indice[3 * faceIndex + 2]	= (calpha + 1) % slideCount + (cbeta - 1) * slideCount + 1;		// next point on slide
								faceIndex++;
							}
							
						   vertice[4 * indexCount] 		= P.x;
						   vertice[4 * indexCount + 1] 	= P.y;
						   vertice[4 * indexCount + 2] 	= P.z;
						   vertice[4 * indexCount + 3]  = 1.f;
							   
						   P.normalize();
						   
						   normals[3 * indexCount] 		= P.x;
						   normals[3 * indexCount + 1] 	= P.y;
						   normals[3 * indexCount + 2]	= P.z;

						   ++indexCount;
						}
						else // top point
						{
							// add top point to vertex array only once
							if(calpha == 0)
							{
								vertice[4 * indexCount] 		= P.x;
								vertice[4 * indexCount + 1] 	= P.y;
								vertice[4 * indexCount + 2] 	= P.z;
								vertice[4 * indexCount + 3]  	= 1.f;
									   
								P.normalize();
								   
								normals[3 * indexCount] 		= P.x;
								normals[3 * indexCount + 1] 	= P.y;
								normals[3 * indexCount + 2]		= P.z;

								++indexCount;								
							}
						}
					}
					else // else bottom point ... do not add any face
					{
						// add point to vertex array only once
						if(calpha == 0)
						{
							vertice[4 * indexCount] 		= P.x;
							vertice[4 * indexCount + 1] 	= P.y;
							vertice[4 * indexCount + 2] 	= P.z;
							vertice[4 * indexCount + 3]  	= 1.f;
								   
							P.normalize();
							   
							normals[3 * indexCount] 		= P.x;
							normals[3 * indexCount + 1] 	= P.y;
							normals[3 * indexCount + 2]		= P.z;
							++indexCount;							
						}
					}
				}
			}
		
			VertexArray vertexArray = new VertexArray();
			for(int index = 0; index < verticeCount; ++index)
			{
				Vertex vertex = new Vertex();
				vertex._coordinate = new Vector4f(vertice[4 * index], vertice[4 * index + 1], vertice[4 * index + 2], vertice[4 * index + 3]);
				vertex._color = new Vector4f(1.f, 1.f, 1.f, 1.f);
				vertex._normal = new Vector3f(normals[3 * index], normals[3 * index + 1], normals[3 * index + 2]);
				vertex._texCoord = new Vector2f(0.f, 0.f);
				
				vertexArray.push_back(vertex);
			}
		
			IndexArray indexArray = new IndexArray();
			for(int index = 0; index < faceCount * 3; ++index)
			{
				indexArray.push_back(indice[index]);
			}
			
			setVertexArray(vertexArray);
			setIndexArray(indexArray);
		}	
	}
}
